package com.auntvt.timo.jpax.core.utils;

import com.auntvt.timo.jpax.core.exception.JpaxRepositoryException;
import com.auntvt.timo.jpax.core.exception.JpaxRepositoryNimietyException;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.annotation.Primary;
import org.springframework.util.TypeUtils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * Repository接口操作工具
 *
 * @author 小懒虫 <auntvt@163.com>
 * @date 2021/5/26
 */
public class RepositoryUtil {

    /**
     * 获取service泛型匹配的repository接口
     *
     * @param baseRepositoryClass repository基类
     * @param serviceClass        service类
     * @param <R>                 repository基类
     * @return repository接口对象
     */
    public static <R> R getMatchRepository(Class<R> baseRepositoryClass, Class<?> serviceClass) {
        // 获取继承自<R>baseRepositoryClass的接口集合
        Map<String, R> repositoryMap = SpringContextUtil.getApplicationContext().getBeansOfType(baseRepositoryClass);

        // 接口是否使用@Primary注解
        AtomicReference<R> primaryRepository = new AtomicReference<>();

        // 筛选基于泛型实体类T的repository接口
        List<R> repositories = repositoryMap.values().stream().filter(proxyRepository -> {
            // 判断是否为代理对象
            if (AopUtils.isAopProxy(proxyRepository)) {
                Class<?>[] interfaces = AopProxyUtils.proxiedUserInterfaces(proxyRepository);
                if (interfaces.length > 0) {
                    Class<?> realizeRepository = interfaces[0];
                    Type[] genericInterfaces = realizeRepository.getGenericInterfaces();
                    for (Type genericInterface : genericInterfaces) {
                        ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
                        Type rawType = parameterizedType.getRawType();
                        // 判断当前Repository接口泛型是否和ServiceImpl实现类泛型一致
                        if (((Class<?>) rawType).isAssignableFrom(baseRepositoryClass) && TypeUtils.isAssignable(genericInterface, serviceClass.getGenericSuperclass())) {
                            // 判断当前Repository接口是否使用@Primary注解，是则使用当前接口
                            if (realizeRepository.isAnnotationPresent(Primary.class)) {
                                primaryRepository.set(proxyRepository);
                            }

                            return true;
                        }
                    }
                }
            }
            return false;
        }).collect(Collectors.toList());

        if (repositories.size() == 0) {
            throw new JpaxRepositoryException("没有找到与[{0}{1}]泛型一致的Repository接口", serviceClass.getName(), TypeUtil.formatGenericToString(serviceClass));
        }

        // 判断匹配当前实体类T的repository接口数量是否多于一个，是则抛出异常提醒
        if (repositories.size() > 1 && primaryRepository.get() == null) {
            throw new JpaxRepositoryNimietyException("发现多个与[{0}{1}]泛型一致的Repository接口，请使用SimpleServiceImpl基类，或使用@Primary标注Repository接口，或重写getRepository()方法",
                    serviceClass.getName(), TypeUtil.formatGenericToString(serviceClass));
        }

        return primaryRepository.get() != null ? primaryRepository.get() : repositories.get(0);
    }
}
